



####################################################################################################################
#
#   Additional help file for "Parties’ voter targeting strategies. What can Facebook ads tell us?" 
#
#   published in Electoral Studies 
#   
#   by Simon Stuckelberger and Jelle Koedam 
#
####################################################################################################################

# This script provides code on how to access the API of the Facebook Ad library.The purpose of this script is not to allow for replication
# (for this see the replication file) but to provide exemplary script on how to access the API. 

####################################################################################################################

rm(list=ls())

install.packages("pacman")

#install not yet installed packages and load all packages 
pacman::p_load(plyr,dplyr, tidyr, lubridate, jsonlite, httr)


#To access the API, one needs to register with an identity document, create a Facebook developer account and create an app (https://www.facebook.com/ads/library/api). 
#The app does not need any content, but one needs to indicate an URL, which describes the privacy policy of the app.

#To access the API one needs to retrieve the access token from https://developers.facebook.com/tools/explorer/, user token
url  <- "https://graph.facebook.com"
path <- "v3.3/ads_archive"
token.f<-""

options(stringsAsFactors = FALSE)
options(scipen=999)

# Example: Austria -----------------------------------------------------------------

#report (from: https://www.facebook.com/ads/library/report/)
report.AUT<- read.csv("replication_access_API_data.csv",encoding = "UTF-8")
report.AUT$Number.of.Ads.in.Library<-as.numeric(as.character(report.AUT$Number.of.Ads.in.Library))
report.AUT<-report.AUT[order(-report.AUT$Number.of.Ads.in.Library),]
report.AUT$Amount.Spent..EUR.<-as.numeric(as.character(report.AUT$Amount.Spent..EUR.))
report.AUT<-report.AUT[order(-report.AUT$Amount.Spent..EUR.),]

##select pages 
sel.AUT<-report.AUT %>%
  filter(Page.Name %in% c("SPÖ","Pamela Rendi-Wagner","ÖVP","Sebastian Kurz","Wir für Kurz","ÖVP für Sebastian Kurz","Die Grünen","Werner Kogler","JETZT - Liste Pilz","Peter Pilz","NEOS","Beate Meinl-Reisinger","FPÖ","Norbert Hofer","HOFER Österreich"))

sel.AUT<-subset(sel.AUT,!is.na(sel.AUT$Amount.Spent..EUR.))
names(sel.AUT)[1]<-c("Page.ID")
sel.AUT<-sel.AUT %>% distinct(sel.AUT$Page.ID,.keep_all = TRUE)
sel.AUT<-sel.AUT %>% filter(Amount.Spent..EUR.>1000) ##filter two smaller sites out without disclaimer

#define facebook function
fb<-function(x) {GET(url=url,path=path,query=list(access_token=token.f,ad_reached_countries="AT",search_page_ids=x,ad_active_status="INACTIVE",ad_delivery_date_min="2019-05-19",ad_delivery_date_max="2019-09-28",fields="page_name,ad_delivery_start_time,ad_delivery_stop_time,
                                                  ad_creative_body,demographic_distribution,region_distribution,impressions,spend,ad_snapshot_url,ad_creative_link_caption,ad_creative_link_description,ad_creative_link_title,publisher_platforms"))
}

#repeat function for every ID
CNT<-lapply(sel.AUT$Page.ID,fb)

#prepare result variable
AUT<-NULL

#loop over each page ID 
for(i in 1:length(sel.AUT$Page.ID)) {
  
  #STEP 1: Prepare first 25 ads 
  ads<-rawToChar(CNT[[i]]$content) #Parsing it to JSON
  ADS <- fromJSON(ads) #transform string into a list
  ads.data<-ADS$data #  #extract data contained in list
  
  
  #Step 2: based on $paging, access and prepare rest of ads 
  while(ADS$paging$'next'!=""){
    
    nextads <- GET(ADS$paging$'next')
    ads <- rawToChar(nextads$content) 
    ADS <- fromJSON(ads)
    
    if (is.null(ADS$paging)) {                #quit loop once paging is null and  add all together into one data frame
      if (is.null(AUT)) { 
        AUT<-ads.data
      } else {
        AUT<-bind_rows(AUT,ads.data)
              }
      break}
    
    ads.data2 <- ADS$data
    ads.data<-bind_rows(ads.data,ads.data2)     #add  data to existing data frame and repeat
    
  }}

rm(list=setdiff(ls(), c("AUT","sel.AUT","report.AUT")))

#remove Ads without info on audiences 
AUT<-AUT %>% 
  drop_na(demographic_distribution)

#turn lists (demography,  region, platforms) and dataframes (impressions and spend) into normal columns
#impressions
AUT$impressions_low<-AUT$impressions$lower_bound
AUT$impressions_upp<-AUT$impressions$upper_bound

#spend
AUT$spend_low<-AUT$spend$lower_bound
AUT$spend_upp<-AUT$spend$upper_bound
AUT<-AUT %>% 
  select(-spend,-impressions)


#demographic_distribution
AUT$ID<-rownames(AUT)
AUT<-unnest(AUT,demographic_distribution,.id="ID")
AUT$genderAge<-with(AUT,paste0(gender,age))
AUT$age<-NULL
AUT$gender<-NULL
AUT<-pivot_wider(AUT,names_from=genderAge,values_from=percentage)

#region_distribution
AUT$ID<-rownames(AUT)
AUT<-unnest(AUT,region_distribution,.id="ID")
AUT<-pivot_wider(AUT,names_from=region,values_from=percentage)

#platform
AUT$platform <- sapply(AUT$publisher_platforms, paste, collapse=",")
AUT<-AUT %>% 
  select(-publisher_platforms)
